package com.borqs.samples;

import android.app.*;
import android.content.*;
import java.util.*;
import android.util.*;
import java.io.*;
import android.os.*;

public class SampleService extends Service {
    private final static String TAG = "SampleService";
    private final static String POWER_SUPPLY_CHARGE_NOW = "POWER_SUPPLY_CHARGE_NOW";
    private final static String POWER_SUPPLY_CHARGE_FULL = "POWER_SUPPLY_CHARGE_FULL";
    private final static long UEVENT_VALID_TIME = 1*60*1000;//10m
    private final static long SAMPLE_INTERVAL = 60*1000;//1m
    private final static double AVERAGE_V_THRESHOLD = 14;//mah

    private final static long SAMPLE_STOP_REASON_V = 0;
    private final static long SAMPLE_STOP_REASON_POWER_CONNECTED = 1;
    private final static long SAMPLE_STOP_REASON_SCREEN_ON = 2;

    private IBinder mBinder;
    private DBHelper helper;

    private Handler mHandler;
    private IntentReceiver mIntentReceiver;
    private PowerManager pm;
    private PowerManager.WakeLock mSamplingWakelock;
    private SampleTask mSampleTask;

    private boolean isSampling;
    private boolean isScreenOn;
    private boolean isOnBattery;
    private boolean isUEventObserving;

    private long mSampleStopReason;
    
    private long mUEventBatteryFull;//nearly constant

    private long mUEventTrackCharge;//pre change now
    private long mUEventTrackTime;//pre uevent update time
    private long mAverageStartCharge;
    private long mAverageStartTime;
    private long mAverageScreenOnChargeDelta;
    private long mAverageScreenOnTimeDelta;

    private long mScreenOnCharge;
    private long mScreenOnTime;

    private List<ASampler> mSamplers;
    private long mSampleSessionId;

    @Override
    public void onCreate(){
        super.onCreate();
        Log.d(TAG,"sample service started ...");
        mHandler = new Handler();
        mIntentReceiver = new IntentReceiver();
        mBinder = new SamplingBinder();
        helper = new DBHelper(this);
        mSampleTask = new SampleTask();

        pm = (PowerManager)getSystemService(Context.POWER_SERVICE);
        mSamplingWakelock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, 
                "SampleService");

        mSamplers = new ArrayList<ASampler>();
        mSamplers.add(new ProcessSampler(helper));
        mSamplers.add(new AlarmSampler(helper));
        mSamplers.add(new WakelockSampler(helper));
        
    }
    @Override
    public IBinder onBind(Intent intent){
        return mBinder;
    }
    class SamplingBinder extends Binder {
        SampleService getService() {
            return SampleService.this;
        }
    }
    class SampleTask implements Runnable{
        long sessionId;

        public void run(){
            long now = SystemClock.elapsedRealtime();
            sample();
            //next sample?
            if(isSampling){
                //long interval = UEVENT_VALID_TIME / 5;
                //if(interval == 0) interval = 1000;//for debug
                mHandler.postDelayed(mSampleTask,SAMPLE_INTERVAL);
            }
            if(mSamplingWakelock.isHeld()){
                mSamplingWakelock.release();
            }
        }
        private void sample(){
            Log.d(TAG,"sample: task run");
            /*sample work should do here*/
            long now = SystemClock.elapsedRealtime();//sample time
            long sample_id = dbInsertSample(sessionId,now);
            for(ASampler sampler : mSamplers){
                //children sample
                sampler.sample(sample_id);
            }
        }
    }
    private long dbInsertSample(long sessionId,long time){
        ContentValues cv = new ContentValues();
        cv.put("time",time);
        cv.put("session_id",sessionId);
        return helper.getWritableDatabase().insert("samples",null,cv);
    }
    private long startSampleSession(double averageV,long charge){
        Log.d(TAG,"sample: start session");
        mSamplingWakelock.acquire(); 
        isSampling = true;

        ContentValues cv = new ContentValues();
        cv.put("start_average_v",averageV);
        cv.put("start_charge",charge);
        cv.put("start_time",SystemClock.elapsedRealtime());
        cv.put("stop_time",0);
        long session_id = helper.getWritableDatabase().insert("sessions",null,cv);

        mSampleTask.sessionId = session_id;
        mHandler.post(mSampleTask);
        return session_id;
    }
    private void stopSampleSession(long sessionId,double averageV,long charge,long stopReason){
        Log.d(TAG,"sample: stop session,mSampleStopReason="+mSampleStopReason);
        isSampling = false;
        ContentValues cv = new ContentValues();
        cv.put("stop_average_v",averageV);
        cv.put("stop_charge",charge);
        cv.put("stop_time",SystemClock.elapsedRealtime());
        cv.put("stop_reason",stopReason);
        helper.getWritableDatabase().update("sessions",cv,"id="+sessionId,null);
    }

    class IntentReceiver extends BroadcastReceiver{
        public IntentReceiver(){
            IntentFilter filter = new IntentFilter();
            filter.addAction(Intent.ACTION_SCREEN_ON);
            filter.addAction(Intent.ACTION_SCREEN_OFF);
            filter.addAction(Intent.ACTION_BATTERY_CHANGED);
            registerReceiver(this,filter);
        }
        @Override
        public void onReceive(Context context,Intent intent){
            String action = intent.getAction();
            if(action.equals(Intent.ACTION_BATTERY_CHANGED)){
                int battery_status = intent.getIntExtra(BatteryManager.EXTRA_STATUS,-1);
                Log.d(TAG,"battery status:"+battery_status);
                if(battery_status == BatteryManager.BATTERY_STATUS_CHARGING){
                    Log.d(TAG,"...battery status is charging...");
                    if(isOnBattery){
                        isOnBattery = false;
                        if(isUEventObserving){
                            Log.d(TAG,"... stop observing ...");
                            mPowerSupplyObserver.stopObserving();
                            isUEventObserving = false;
                        }
                        if(isSampling) {
                            //stop sample because of charging
                            mSampleStopReason = SAMPLE_STOP_REASON_POWER_CONNECTED;
                            stopSampleSession(mSampleSessionId,0,0,mSampleStopReason);
                        }
                    }
                }
                if(battery_status == BatteryManager.BATTERY_STATUS_DISCHARGING){
                    Log.d(TAG,"...battery status is discharging...");
                    if(!isOnBattery){
                        isOnBattery = true;
                        /*init*/
                        mAverageStartCharge = mUEventTrackCharge = 0;
                        mAverageStartTime = mUEventTrackTime = 0;
                        mAverageScreenOnChargeDelta = 0;
                        mAverageScreenOnTimeDelta = 0;
                        if(!isUEventObserving){
                            Log.d(TAG,"... start observing ...");
                            mPowerSupplyObserver.startObserving("SUBSYSTEM=power_supply");
                            isUEventObserving = true;
                        }
                    }
                }
            }
            if(action.equals(Intent.ACTION_SCREEN_ON)){
                Log.d(TAG,"#screen on#");
                if(isOnBattery){
                    mScreenOnCharge = mUEventTrackCharge;
                    mScreenOnTime = mUEventTrackTime;
                    isScreenOn = true;
                    if(isSampling){
                        mSampleStopReason = SAMPLE_STOP_REASON_SCREEN_ON;
                        stopSampleSession(mSampleSessionId,0,0,mSampleStopReason);
                    }
                }
            }
            if(action.equals(Intent.ACTION_SCREEN_OFF)){
                Log.d(TAG,"#screen off#");
                if(isOnBattery){
                    mAverageScreenOnChargeDelta += (mScreenOnCharge - mUEventTrackCharge);
                    mAverageScreenOnTimeDelta += (mUEventTrackTime - mScreenOnTime);
                    isScreenOn = false;
                    //should we start sample again?
                    if(!isSampling && mSampleStopReason == SAMPLE_STOP_REASON_SCREEN_ON){
                        restartSample();
                    }
                }
            }
        }
    }
    private void restartSample(){
        Log.d(TAG,"sample: restart");
        mSamplingWakelock.acquire(); 
        isSampling = true;
        /*use pre session id*/
        mSampleTask.sessionId = mSampleSessionId;
        mHandler.post(mSampleTask);
    }

    private UEventObserver mPowerSupplyObserver = new UEventObserver() {
        @Override
        public void onUEvent(UEventObserver.UEvent event) {
            Log.d(TAG,"===>>>uevent");
            if(mUEventBatteryFull == 0){
                String bf = event.get(POWER_SUPPLY_CHARGE_FULL);
                if(bf != null)
                    mUEventBatteryFull = Long.parseLong(bf);
            }
            String battery_now_s = event.get(POWER_SUPPLY_CHARGE_NOW);
            if(battery_now_s != null){
                long battery_now = Long.parseLong(battery_now_s);
                long battery_now_time = SystemClock.elapsedRealtime();

                Log.d(TAG,"status: battery_now="+battery_now+",battery_now_time="+battery_now_time);
                Log.d(TAG,"status: mUEventTrackTime="+mUEventTrackTime+",mAverageStartTime="+mAverageStartTime);
                Log.d(TAG,"status: mUEventTrackCharge="+mUEventTrackCharge+",mAverageStartCharge="+mAverageStartCharge);
                if(mUEventTrackCharge == 0 || mUEventTrackTime == 0){
                    Log.d(TAG,"#uevent init#");
                    /*init*/
                    mAverageStartCharge = mUEventTrackCharge = battery_now;
                    mAverageStartTime = mUEventTrackTime = battery_now_time;
                    mAverageScreenOnChargeDelta = 0;
                    mAverageScreenOnTimeDelta = 0;
                }else{
                    /*track*/
                    long track_delta = mUEventTrackCharge - battery_now;
                    if(track_delta > 0){
                        long track_delta_time = battery_now_time - mUEventTrackTime;
                        double ma_track_delta = (double)track_delta / 1000;
                        double h_track_delta_time = (double)track_delta_time / (1000 * 60 * 60);
                        double track_v_mah = ma_track_delta / h_track_delta_time;
                        Log.d(TAG,"track: ma_delta="+ma_track_delta+",h_delta_time="+h_track_delta_time+",v="+track_v_mah +"(mah)");
                        mUEventTrackCharge = battery_now;
                        mUEventTrackTime = battery_now_time;
                    }

                    /*average*/
                    long average_delta_time = battery_now_time - mAverageStartTime;
                    Log.d(TAG,"average: delta_time="+average_delta_time);
                    if(average_delta_time >= UEVENT_VALID_TIME && !isScreenOn){
                        long average_delta = mAverageStartCharge - battery_now;
                        /*substract screen on delta*/
                        average_delta -= mAverageScreenOnChargeDelta;
                        average_delta_time -= mAverageScreenOnTimeDelta;
                        double ma_average_delta = (double)average_delta / 1000;
                        double h_average_delta_time = (double)average_delta_time / (1000 * 60 * 60);
                        double average_v_mah = ma_average_delta / h_average_delta_time;
                        Log.d(TAG,"average: ma_delta="+ma_average_delta+",h_delta_time="+h_average_delta_time+",v="+average_v_mah+"(mah)");
                        if(average_v_mah > AVERAGE_V_THRESHOLD){
                            if(!isSampling){
                                mSampleSessionId = startSampleSession(average_v_mah,battery_now);
                            }
                        }else{
                            if(isSampling){
                                mSampleStopReason = SAMPLE_STOP_REASON_V;
                                stopSampleSession(mSampleSessionId,average_v_mah,battery_now,mSampleStopReason );
                            }
                        }
                        /*new average segment start*/
                        mAverageStartCharge = battery_now;
                        mAverageStartTime = battery_now_time;
                        mAverageScreenOnChargeDelta = 0;
                        mAverageScreenOnTimeDelta = 0;
                    }
                }
            }
        }
    };
}
